#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include "MvCameraControl.h"
#include <opencv2/opencv.hpp>
#include <sys/stat.h>  // For mkdir
#include <sys/types.h> // 用于创建目录
#include <iostream>
#include <filesystem>
#include <mutex>


// v3的改动
//主要手动实现将pData转为RGB并保存，放弃调用海康保存RGB的函数。



//定义像素类型宏，确保与SDK一致 
#define CAMERA_NUM             3      //几路相机
#define PIXEL_TYPE_BAYER_RG8 17301513 // BayerRG8 的示例值
#define PIXEL_TYPE_RGB8_PACKED 35127316 // RGB8_Packed 的示例值


bool g_bExit = false;

struct ImageData {
    cv::Mat image;
    std::string filePath;
}

//全局队列和同步工具



struct ThreadParams {
    void* handle;
    const char* cameraIp;
};

std::string GetCurrentTimestamp(){
    // 获取当前时间点
    auto now = std::chrono::system_clock::now();
    auto now_time_t = std::chrono::system_clock::to_time_t(now);

    //转换为本地时间
    std::tm localTime;
    localtime_r(&now_time_t,&localTime);

    //获取毫秒部分
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
    
    // 格式化时间戳
     std::ostringstream oss;
    oss << std::put_time(&localTime, "%Y%m%d%H%M%S")
        << std::setw(3) << std::setfill('0'); // 添加毫秒部分
    // std::ostringstream oss;
    // oss << std::put_time(&localTime, "%Y%m%d%H%M%S")
    //     << std::setw(3) << std::setfill('0') << milliseconds.count(); // 添加毫秒部分
    
    return oss.str();

}

// 等待用户输入enter键来结束取流或结束程序
// wait for user to input enter to stop grabbing or end the sample program
void PressEnterToExit(void)
{
    int c;
    while ( (c = getchar()) != '\n' && c != EOF );
    fprintf( stderr, "\nPress enter to exit.\n");
    while( getchar() != '\n');
    g_bExit = true;
    sleep(1);
}

bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{
    if (NULL == pstMVDevInfo)
    {
        printf("The Pointer of pstMVDevInfo is NULL!\n");
        return false;
    }
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    {
        int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
        int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
        int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
        int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

        // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);
        printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
        printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
    }
    else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    {
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
        printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
    }
    else
    {
        printf("Not support.\n");
    }

    return true;
}


std::string GetCameraIp(MV_CC_DEVICE_INFO* pstMVDevInfo)
{
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    {
        int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
        int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
        int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
        int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);
        char ipStr[20];
        snprintf(ipStr, sizeof(ipStr), "%d.%d.%d.%d", nIp1, nIp2, nIp3, nIp4);
        return std::string(ipStr);
    }
    return "";
}


void SaveImageToFile(const unsigned char* pData, int width, int height,long int pixelType, const char* dirPath, int frameNum)
{
    // 创建一个 RGB 格式的 Mat 对象
    cv::Mat rgbImage;

    if (pixelType==PIXEL_TYPE_RGB8_PACKED){
        // 如果是 RGB8 格式，直接构建三通道 Mat
        rgbImage = cv::Mat(height, width, CV_8UC3, (void*)pData);
        cv::cvtColor(rgbImage, rgbImage, cv::COLOR_BGR2RGB); // 或其他适当的转换
    }
     else if(pixelType == PIXEL_TYPE_BAYER_RG8){ 
        // 如果是 Bayer 格式，创建单通道 Mat 并转换为 RGB
        cv::Mat bayerImage(height, width, CV_8UC1, (void*)pData);
        cv::cvtColor(bayerImage, rgbImage, cv::COLOR_BayerRG2RGB);
    }
    else{
        std::cerr << "Unsupported pixel type: " << pixelType << std::endl;
        return;
    }

    // // 轻微调整对比度和亮度（可选）
    // cv::Mat adjustedImage;
    // rgbImage.convertTo(adjustedImage, -1, 1.1, 10); // 增加对比度和亮度，但不做过多调整

    // 调用时间戳函数
    std::string timestamp = GetCurrentTimestamp();

    // 构造保存路径
    char imagePath[256];
    snprintf(imagePath, sizeof(imagePath), "%s/images_%s_%04d.png", dirPath, timestamp.c_str(),frameNum);

    // 保存解码后的 RGB 图像
    cv::imwrite(imagePath, rgbImage);

    std::cout << "Image saved successfully: " << imagePath << std::endl;
}


// 采用PixelType_Gvsp_RGB8_Packed 相机出图得话，存在数据丢包的情况，图像中存在条纹。
// // 全局互斥锁用于保护文件保存操作
std::mutex saveImageMutex;
void SaveImageUsingMV(void* pUser, const unsigned char* pData, MV_FRAME_OUT_INFO_EX* pstImageInfo, const char* dirPath, int frameNum)
{
    int nRet = MV_OK;

    // 创建保存参数结构体并初始化
    MV_SAVE_IMG_TO_FILE_PARAM stSaveParam;
    memset(&stSaveParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));

    // 设置图像保存类型
    stSaveParam.enImageType = MV_Image_Jpeg;
    stSaveParam.pData = const_cast<unsigned char*>(pData);
    stSaveParam.nDataLen = pstImageInfo->nFrameLen;
    stSaveParam.nWidth = pstImageInfo->nWidth;
    stSaveParam.nHeight = pstImageInfo->nHeight;
    stSaveParam.enPixelType = pstImageInfo->enPixelType;
    stSaveParam.nQuality = 80; // 设置JPEG质量

    // 调用时间戳函数
    std::string timestamp = GetCurrentTimestamp();

    // 构造保存路径
    char imagePath[256];
    snprintf(imagePath, sizeof(imagePath), "%s/images_%s_%04d.jpg", dirPath, timestamp.c_str(),frameNum);

    // 确保路径字符串以 '\0' 结尾
    strncpy(stSaveParam.pImagePath, imagePath, sizeof(stSaveParam.pImagePath) - 1);
    stSaveParam.pImagePath[sizeof(stSaveParam.pImagePath) - 1] = '\0';

    // 加锁以确保文件写入的线程安全性
    {
        std::lock_guard<std::mutex> lock(saveImageMutex);
        nRet = MV_CC_SaveImageToFile(pUser, &stSaveParam);
    }

    if (MV_OK != nRet)
    {
        printf("Save Image Failed! Error code: 0x%x\n", nRet);
    }
    else
    {
        printf("Image saved successfully: %s\n", imagePath);
    }
}

static void* WorkThread(void* pUserData)
{   
    ThreadParams* params = static_cast<ThreadParams*>(pUserData);
    void* pUser = params->handle;
    const char* dirPath = params->cameraIp;
    
    int nRet = MV_OK;

    MVCC_STRINGVALUE stStringValue = {0};
    char camSerialNumber[256] = {0};
    nRet = MV_CC_GetStringValue(pUser, "DeviceSerialNumber", &stStringValue);
    if (MV_OK == nRet)
    {
        memcpy(camSerialNumber, stStringValue.chCurValue, sizeof(stStringValue.chCurValue));
    }
    else
    {
        printf("Get DeviceUserID Failed! nRet = [%x]\n", nRet);
    }

    // ch:获取数据包大小 | en:Get payload size
    MVCC_INTVALUE stParam;
    memset(&stParam, 0, sizeof(MVCC_INTVALUE));
    nRet = MV_CC_GetIntValue(pUser, "PayloadSize", &stParam);
    if (MV_OK != nRet)
    {
        printf("Get PayloadSize fail! nRet [0x%x]\n", nRet);
        return NULL;
    }

    MV_FRAME_OUT_INFO_EX stImageInfo = {0};
    memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
    unsigned char * pData = (unsigned char *)malloc(sizeof(unsigned char) * stParam.nCurValue);
    if (NULL == pData)
    {
        return NULL;
    }
    unsigned int nDataSize = stParam.nCurValue;

    
    // MVCC_STRINGVALUE stIpValue = {0};
    // // const char* dirPath = "./images";
    // nRet = MV_CC_GetStringValue(pUser, "GevCurrentIPAddress", &stIpValue);
    // if (MV_OK != nRet)
    // {
    //     printf("Get Camera IP failed! nRet = [%x]\n", nRet);
    //     return NULL;
    // }
    // const char* dirPath = stIpValue.chCurValue;


    int frameNum = 0;
    while(1)
    {
		if(g_bExit)
		{
			break;
		}
			
        nRet = MV_CC_GetOneFrameTimeout(pUser, pData, nDataSize, &stImageInfo, 1000);
        if (nRet == MV_OK)
        {
            printf("Cam Serial Number[%s]:GetOneFrame, Width[%d], Height[%d], nFrameNum[%d]\n", 
                camSerialNumber, stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nFrameNum);
            printf("PixelType: [%ld]", stImageInfo.enPixelType);

            SaveImageToFile(pData, stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.enPixelType, dirPath, frameNum++);
            // SaveImageUsingMV(pUser, pData, &stImageInfo, dirPath, frameNum++);
        }
        else
        {
            printf("cam[%s]:Get One Frame failed![%x]\n", camSerialNumber, nRet);
        }
    }

    if(pData != NULL)
    {
        free(pData);
        pData = NULL;
    }

    return 0;
}

int main()
{
	int nRet = MV_OK;

    void* handle[CAMERA_NUM] = {NULL};
    std::string cameraIps[CAMERA_NUM];
    do
    {   
        // ch:初始化SDK | en:Initialize SDK
	    nRet = MV_CC_Initialize();
	    if (MV_OK != nRet)
	    {
		    printf("Initialize SDK fail! nRet [0x%x]\n", nRet);
		    return nRet;
	    }

        MV_CC_DEVICE_INFO_LIST stDeviceList;
        memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));

        // 枚举设备
        // enum device
        nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
        if (MV_OK != nRet)
        {
            printf("MV_CC_EnumDevices fail! nRet [%x]\n", nRet);
            return nRet;
        }
        unsigned int nIndex = 0;
        if (stDeviceList.nDeviceNum > 0)
        {
            for (int i = 0; i < stDeviceList.nDeviceNum; i++)
            {
                printf("[device %d]:\n", i);
                MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
                if (NULL == pDeviceInfo)
                {
                    break;
                } 
                PrintDeviceInfo(pDeviceInfo);            
            }  
        } 
        else
        {
            printf("Find No Devices!\n");
            return -1;
        }

        if(stDeviceList.nDeviceNum < CAMERA_NUM)
        {
            printf("only have %d camera\n", stDeviceList.nDeviceNum);
            return -1;
        }
	
	    // 提示为多相机测试
	    // Tips for multicamera testing
	    printf("Start %d camera Grabbing Image test\n", CAMERA_NUM);

        for(int i = 0; i < CAMERA_NUM; i++)
        {
            printf("Please Input Camera Index: ");
            scanf("%d", &nIndex);

            // 选择设备并创建句柄
            // select device and create handle
            nRet = MV_CC_CreateHandle(&handle[i], stDeviceList.pDeviceInfo[nIndex]);
            if (MV_OK != nRet)
            {
                printf("MV_CC_CreateHandle fail! nRet [%x]\n", nRet);
                break;
            }

            // 打开设备
            // open device
            nRet = MV_CC_OpenDevice(handle[i]);
            if (MV_OK != nRet)
            {
                printf("MV_CC_OpenDevice fail! nRet [%x]\n", nRet);
                break;
            }


            // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
            if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
            {
                int nPacketSize = MV_CC_GetOptimalPacketSize(handle[i]);
                if (nPacketSize > 0)
                {
                    nRet = MV_CC_SetIntValueEx(handle[i],"GevSCPSPacketSize",nPacketSize);
                    if(nRet != MV_OK)
                    {
                        printf("Warning: Set Packet Size fail nRet [0x%x]!\n", nRet);
                    }
                }
                else
                {
                    printf("Warning: Get Packet Size fail nRet [0x%x]!\n", nPacketSize);
                }
            }

            //获取相机IP地址
            cameraIps[i] = GetCameraIp(stDeviceList.pDeviceInfo[nIndex]);
            if (!cameraIps[i].empty()){
                char dirPath[256];
                snprintf(dirPath,sizeof(dirPath),"./%s",cameraIps[i].c_str());
                mkdir(dirPath,0755);
                printf("Saving images to folder: %s\n", dirPath);
            }

                        // //设置图像格式
            // //设置Enum型参数-相机图像格式
            // //注意点1：相机图像格式设置时，只有在MV_CC_Startgrab接口调用前才能设置,取流过程中，不能修改图像格式
            // nRet = MV_CC_SetEnumValue(handle[i], "PixelFormat", PixelType_Gvsp_RGB8_Packed);
            // if (MV_OK != nRet)
            // {
            //     printf("error: SetEnumValue fail [%x]\n", nRet);
            // }

            char configFileName[256];
            std::string configDir = "../configs";
            snprintf(configFileName, sizeof(configFileName), "%s/%s_config.ini", configDir.c_str(),cameraIps[i].c_str());
            //下载配置文件
            // 根据 IP 命名配置文件
            // if (!std::filesystem::exists(configDir)) {
            //     if (std::filesystem::create_directory(configDir)) {
            //         std::cout << "Folder created successfully: " << configDir << std::endl;
            //     } 
            //     else {
            //         std::cerr << "Failed to create folder: " << configDir << std::endl;
            //         return -1; // 添加错误处理
            //     }
            // }

            // // 保存配置
            // nRet = MV_CC_FeatureSave(handle[i], configFileName);
            // if (nRet != MV_OK) {
            //     printf("Save Config Failed! nRet = [0x%x]\n", nRet);
            // } else {
            //     printf("Config saved to file: %s\n", configFileName);
            // }

            // 加载配置
            nRet = MV_CC_FeatureLoad(handle[i], configFileName);
            if (nRet != MV_OK) {
                printf("Load Config Failed! nRet = [0x%x]\n", nRet);
            } else {
                printf("Config loaded from file: %s\n", configFileName);
            }

        }

        for(int i = 0; i < CAMERA_NUM; i++)
        {
            // 设置触发模式为off
            // set trigger mode as off
            nRet = MV_CC_SetEnumValue(handle[i], "TriggerMode", MV_TRIGGER_MODE_OFF);
            if (MV_OK != nRet)
            {
                printf("Cam[%d]: MV_CC_SetTriggerMode fail! nRet [%x]\n", i, nRet);
            }


            // 开始取流
            // start grab image
            nRet = MV_CC_StartGrabbing(handle[i]);
            if (MV_OK != nRet)
            {
                printf("Cam[%d]: MV_CC_StartGrabbing fail! nRet [%x]\n",i, nRet);
                break;
            }

            // 创建线程并传递参数
            pthread_t nThreadID;
            ThreadParams params;
            params.handle = handle[i];
            params.cameraIp = cameraIps[i].c_str();

            // nRet = pthread_create(&nThreadID, NULL ,WorkThread , handle[i]);
            nRet = pthread_create(&nThreadID, NULL ,WorkThread , &params);
            if (nRet != 0)
            {
                printf("Cam[%d]: thread create failed.ret = %d\n",i, nRet);
                break;
            }
        }

        PressEnterToExit();

        for(int i = 0; i < CAMERA_NUM; i++)
        {
            // 停止取流
            // end grab image
            nRet = MV_CC_StopGrabbing(handle[i]);
            if (MV_OK != nRet)
            {
                printf("MV_CC_StopGrabbing fail! nRet [%x]\n", nRet);
                break;
            }

            // 关闭设备
            // close device
            nRet = MV_CC_CloseDevice(handle[i]);
            if (MV_OK != nRet)
            {
                printf("MV_CC_CloseDevice fail! nRet [%x]\n", nRet);
                break;
            }

            // 销毁句柄
            // destroy handle
            nRet = MV_CC_DestroyHandle(handle[i]);
            if (MV_OK != nRet)
            {
                printf("MV_CC_DestroyHandle fail! nRet [%x]\n", nRet);
                break;
            }

            handle[i] = NULL;
        }
    }while(0);

    for(int i = 0; i < CAMERA_NUM; i++)
    {
        if(handle[i]!=NULL)
        {
            MV_CC_DestroyHandle(handle[i]);
            handle[i] = NULL;
        }
    }

    // ch:反初始化SDK | en:Finalize SDK
	MV_CC_Finalize();

    printf("exit\n");
    return 0;
}
